﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Rung.EBook.Common.Model;
using Rung.EBook.WebSiteAnalizer;
using Rung.EBook.Common.Driver;
using Rung.EBook.UI.Forms;

namespace Rung.EBook.UI.Controls.Panels
{
    public partial class pnDownloader : UserControl
    {
        /* ---------------------------------------------------------------------------------------------Properties-------- */
        // 在窗体提示框中显示值
        private WriteInfo mWriteInfo;
        private AppendInfo mAppendInfo;
        private ProgressBarReset mProgressBarReset;
        private ProgressBarIncreace mProgressBarIncreace;
        // 多线程改变 mContentWorkers 集合的项数目时，锁定该集合。
        private object OBJ_mContentWorkers = new object();
        // 是否取消采集线程
        private bool isCancel = false;

        BookBase mBookBM;
        public AnalizerBase mAnalizer;
        BackgroundWorker mWorker;
        // 获取正文时，开启的子线程个数
        const int mContentWorkerCount = 10;
        // 获取正文时，开启的子线程集合
        List<BackgroundWorker> mContentWorkers = new List<BackgroundWorker>(mContentWorkerCount);
        // 批量获取正文时，线程完成一次获取动作后的静默时间（防止抓取太猛，把对方服务器搞死）
        // 该值 <= 0时，为全速抓取
        const int mContentWorkerIdleSecond = 3;

        /* ---------------------------------------------------------------------------------------------Constructor------- */
        public pnDownloader()
        {
            InitializeComponent();

            this.mBookBM = new BookBase();
            gvChapters.AutoGenerateColumns = false;
            this.txbContent.KeyDown += new KeyEventHandler(UiHelper.TextBox_KeyDown);
        }

        internal pnDownloader(WriteInfo writeInfo, AppendInfo appendInfo, ProgressBarReset progressBarReset, ProgressBarIncreace progressBarIncreace)
            : this()
        {
            // TODO: Complete member initialization
            this.mWriteInfo = writeInfo;
            this.mAppendInfo = appendInfo;
            this.mProgressBarReset = progressBarReset;
            this.mProgressBarIncreace = progressBarIncreace;
        }

        private void pnDownloader_Load(object sender, EventArgs e)
        {
            this.Dock = DockStyle.Fill;
        }

        /* ---------------------------------------------------------------------------------------------Event Methods----- */
        // 解析器设置
        private void btnAnalizerConfig_Click(object sender, EventArgs e)
        {
            Rung.EBook.UI.Forms.LoadAnalizerForm form = new Rung.EBook.UI.Forms.LoadAnalizerForm(this.mWriteInfo, this.mAppendInfo, OnLoadAnalizerFormClose);
            form.ShowDialog(this.ParentForm);
        }

        // 获取章节
        private void btnGetChapters_Click(object sender, EventArgs e)
        {
            if (this.mAnalizer == null)
            {
                this.mWriteInfo("请先选择解释器。", InfoType.Errer);
                return;
            }

            if (null != this.mWorker && this.mWorker.IsBusy)
            {
                this.mWriteInfo("线程繁忙，请稍等。", InfoType.Errer);
                return;
            }

            try
            {
                // http://hi.baidu.com/wgb603/blog/item/4c58230ac3f3ec8fd1581b5b.html
                this.mBookBM.Chapters.Clear();
                //DataTable table = gvChapters.DataSource as DataTable;
                //if (table != null)
                //{
                //    table.Rows.Clear();
                //    gvChapters.DataSource = table;
                //}

                this.mWriteInfo("初始化下载进程...", InfoType.Process);

                mWorker = new BackgroundWorker();
                mWorker.DoWork += new DoWorkEventHandler(GetChapters_DoWork);
                mWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(GetChapters_RunWorkerCompleted);
                this.mAppendInfo("完成。");

                mWorker.RunWorkerAsync();
            }
            catch (Exception ex)
            {
                this.mWriteInfo("获取章节列表失败：" + ex.Message, InfoType.Errer);
                return;
            }
        }

        #region BackgroundWorker: GetChapters
        void GetChapters_DoWork(object sender, DoWorkEventArgs e)
        {
            this.mWriteInfo("开始获取章节列表...", InfoType.Process);
            this.mAnalizer.FillBook(this.mBookBM);
        }

        void GetChapters_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                this.mWriteInfo("线程错误：" + e.Error.Message, InfoType.Warning);
                return;
            }
            if (e.Cancelled == true)
            {
                this.mWriteInfo("获取章节列表进程取消。", InfoType.Warning);
                return;
            }

            this.mAppendInfo("成功");

            gvChapters.DataSource = null;
            gvChapters.DataSource = this.mBookBM.Chapters;

            this.WorkerDispose(sender);
        }
        #endregion

        // 获取章节正文（章节列表选择事件）
        private void gvChapters_CurrentCellChanged(object sender, EventArgs e)
        {
            if (null != this.mWorker && this.mWorker.IsBusy)
            {
                this.mWriteInfo("线程繁忙，请稍等。", InfoType.Errer);
                return;
            }

            if (gvChapters.SelectedRows.Count <= 0) return;

            ChapterBase cBM = gvChapters.SelectedRows[0].DataBoundItem as ChapterBase;

            if (cBM != null)
            {
                if (string.IsNullOrEmpty(cBM.Content))
                {
                    try
                    {
                        // 初始化主窗口进度条值域
                        this.mProgressBarReset(1);

                        // 修改本章节列表状态为 Downloading
                        this.gvChapters_StatusChanged(gvChapters.SelectedRows[0].Cells[0], ChapterStatus.Downloading);

                        this.mWriteInfo("初始化下载进程...", InfoType.Process);
                        mWorker = new BackgroundWorker();
                        mWorker.DoWork += new DoWorkEventHandler(GetContent_DoWork);
                        mWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(GetContent_RunWorkerCompleted);
                        this.mAppendInfo("完成。");

                        mWorker.RunWorkerAsync(gvChapters.SelectedRows[0]);
                    }
                    catch (Exception ex)
                    {
                        this.mWriteInfo("获取章节正文失败：" + ex.Message, InfoType.Errer);
                        return;
                    }
                }
                else
                    txbContent.Text = cBM.Content;
            }
            else
                this.mWriteInfo("系统错误：章节列表选定行的绑定数据为空。", InfoType.Errer);
        }

        #region BackgroundWorker: GetContent
        void GetContent_DoWork(object sender, DoWorkEventArgs e)
        {
            DataGridViewRow row = e.Argument as DataGridViewRow;

            if (row != null)
            {
                ChapterBase cBM = row.DataBoundItem as ChapterBase;
                if (cBM == null)
                {
                    this.mWriteInfo("系统错误：gvChapters绑定数据为空。", InfoType.Errer);
                    return;
                }

                this.mWriteInfo(string.Format("开始获取【{0}】内容...", cBM.Title), InfoType.Process);
                this.mAnalizer.FillChapter(cBM);

                if (mContentWorkerIdleSecond > 0)
                    System.Threading.Thread.Sleep(mContentWorkerIdleSecond * 1000);

                e.Result = row;
            }
        }

        void GetContent_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.mProgressBarIncreace();

            DataGridViewRow row = e.Result as DataGridViewRow;

            if (e.Error != null)
            {
                this.gvChapters_StatusChanged(row.Cells[0], ChapterStatus.Unknow);
                this.mWriteInfo("线程错误：" + e.Error.Message, InfoType.Warning);
                return;
            }
            if (e.Cancelled == true)
            {
                this.gvChapters_StatusChanged(row.Cells[0], ChapterStatus.Unknow);
                this.mWriteInfo("获取正文进程取消。", InfoType.Warning);
                return;
            }
            if (row == null)
            {
                //this.gvChapters_StatusChanged(row.Cells[0], ChapterStatus.Unknow);
                this.mWriteInfo("线程错误，线程参数（e.Result as DataGridViewRow）为空。", InfoType.Warning);
                return;
            }

            ChapterBase cBM = row.DataBoundItem as ChapterBase;
            if (cBM == null || string.IsNullOrEmpty(cBM.Content))
            {
                this.gvChapters_StatusChanged(row.Cells[0], ChapterStatus.Empty);
                this.mWriteInfo("获取的正文数据为空。", InfoType.Warning);
                return;
            }

            this.txbContent_Set(cBM.Content);
            this.mWriteInfo("获取正文成功。", InfoType.Success);

            // 修改本章节列表状态为 Filled
            if (string.IsNullOrEmpty(cBM.Content) == false)
                this.gvChapters_StatusChanged(row.Cells[0], ChapterStatus.Filled);
            else
                this.gvChapters_StatusChanged(row.Cells[0], ChapterStatus.Empty);

            this.WorkerDispose(sender);
        }
        #endregion

        // 获取全书
        private void btnGetContents_Click(object sender, EventArgs e)
        {
            if (gvChapters.Rows.Count == 0)
            {
                this.mWriteInfo("请先获取章节列表。", InfoType.Errer);
                return;
            }

            if (null != this.mWorker && this.mWorker.IsBusy)
            {
                this.mWriteInfo("线程繁忙，请稍等。", InfoType.Errer);
                return;
            }

            this.mContentWorkers.Clear();
            // 初始化主窗口进度条值域
            this.mProgressBarReset(this.gvChapters.Rows.Count);

            this.mWorker.DoWork += new DoWorkEventHandler(GetContents_DoWork);
            this.mWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(GetContents_RunWorkerCompleted);
            this.mWorker.RunWorkerAsync();
        }

        #region BackgroundWorker: GetContents
        void GetContents_DoWork(object sender, DoWorkEventArgs e)
        {
            while (true)
            {
                if (isCancel) return;

                BackgroundWorker worker = this.GetIdleWorker();
                if (worker == null) continue;
                DataGridViewRow row = this.GetUnknowRow();
                if (row == null) break; else this.gvChapters_StatusChanged(row.Cells[0], ChapterStatus.Downloading);

                ChapterBase cBM = row.DataBoundItem as ChapterBase;
                if (cBM != null)
                {
                    if (string.IsNullOrEmpty(cBM.Content))
                    {
                        try
                        {
                            this.mWriteInfo(string.Format("初始化章节[{0}]下载进程...", row.Cells[1].Value), InfoType.Process);
                            worker.WorkerSupportsCancellation = true;
                            worker.DoWork += new DoWorkEventHandler(GetContent_DoWork);
                            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(GetContent_RunWorkerCompleted);
                            this.mAppendInfo("完成。");

                            // 修改本章节列表状态为 Downloading
                            this.gvChapters_StatusChanged(row.Cells[0], ChapterStatus.Downloading);

                            // 执行
                            worker.RunWorkerAsync(row);
                        }
                        catch (Exception ex)
                        {
                            string msg = string.Format("获取章节[{0}]正文失败：" + ex.Message, row.Cells[1].Value);
                            this.mWriteInfo(msg, InfoType.Errer);
                            this.gvChapters_StatusChanged(row.Cells[0], ChapterStatus.Unknow);
                            return;
                        }
                    }
                    else
                        txbContent.Text = cBM.Content;
                }
                else
                    this.mWriteInfo(string.Format("系统错误：章节列表项[{0}]绑定数据为空。", row.Cells[1].Value), InfoType.Errer);
            }
        }

        void GetContents_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                this.mWriteInfo("线程错误：" + e.Error.Message, InfoType.Warning);
                return;
            }
            if (e.Cancelled == true)
            {
                this.mWriteInfo("批量获取正文进程取消。", InfoType.Warning);
                return;
            }

            this.mAppendInfo("批量获取完成。");
        }

        // 获取线程池中空闲的线程
        private BackgroundWorker GetIdleWorker()
        {
            lock (OBJ_mContentWorkers)
            {
                if (this.mContentWorkers.Count < mContentWorkerCount)
                {
                    BackgroundWorker worker = new BackgroundWorker();
                    this.mContentWorkers.Add(worker);
                    return worker;
                }
            }

            return null;
        }

        private object OBJ_GetUnknowRow = new object();
        // 获取 GridView 中未知状态的行（尚未开启下载）
        private DataGridViewRow GetUnknowRow()
        {
            lock (OBJ_GetUnknowRow)
            {
                foreach (DataGridViewRow row in gvChapters.Rows)
                {
                    if (null == row.Cells[0].Value || string.IsNullOrEmpty(row.Cells[0].Value.ToString()) || row.Cells[0].Value == "U")
                        return row;
                }
            }

            return null;
        }
        #endregion

        // 保存工程
        private void btnSaveAsProject_Click(object sender, EventArgs e)
        {
            using (SaveFileDialog dialog = new SaveFileDialog())
            {
                dialog.Title = "保存工程";
                dialog.AddExtension = true;
                dialog.CheckPathExists = true;
                dialog.DefaultExt = "ebp";
                dialog.Filter = "RungEBook工程文件|*.ebp";
                dialog.InitialDirectory = Rung.EBook.Common.Driver.StaticFunctions.GetProjectFolderPath();
                dialog.FileName = this.txbBookTitle.Text;// 默认书名

                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    ProjectBM pBM = new ProjectBM();
                    pBM.Book = this.mBookBM;
                    pBM.BookClass = this.mBookBM.GetType().FullName;
                    pBM.BuildTime = DateTime.Now;

                    try
                    {
                        pBM.Save(dialog.FileName);
                        this.mWriteInfo("保存工程成功。", InfoType.Success);
                    }
                    catch (Exception ex)
                    {
                        this.mWriteInfo("保存工程失败：" + ex.Message, InfoType.Errer);
                    }
                }
            }
        }

        // 开发测试
        private void btnDevTest_Click(object sender, EventArgs e)
        {
            #region gvChapters绑定
            List<ChapterBase> cBMs = new List<ChapterBase>();
            cBMs.Add(new ChapterBase { Title = "Test1", Url = "http1" });
            cBMs.Add(new ChapterBase { Title = "Test2", Url = "http2" });
            cBMs.Add(new ChapterBase { Title = "Test3", Url = "http3" });

            gvChapters.DataSource = cBMs;
            #endregion
        }

        // 转到制作
        private void btnTurnToBookMaker_Click(object sender, EventArgs e)
        {
            Rung.EBook.DocFormater.Umd.UmdBookBM umdBM = new Rung.EBook.DocFormater.Umd.UmdBookBM();
            umdBM.FromBookBase(this.mBookBM);

            TabControl tab = (this.ParentForm as Rung.EBook.UI.Forms.MainForm).tcMain;
            pnEBookMaker pn = tab.TabPages[2].Controls[0] as pnEBookMaker;
            pn.LoadBookBM(umdBM);
            tab.SelectedIndex = 2;
        }

        /* ---------------------------------------------------------------------------------------------Private Methods----- */
        // 回溯事件：当 LoadAnalizerForm 窗口关闭时，将选中的解析器传入本窗体。
        public void OnLoadAnalizerFormClose(AnalizerBase analizer)
        {
            if (analizer != null)
            {
                this.mAnalizer = analizer;

                this.txbBookTitle.Text = analizer.BookNamePrefix;
                this.txbChapterRegex.Text = analizer.ChapterRegex;
                this.txbChapterUrl.Text = analizer.ChaptersUrl;
                this.txbContentBaseUrl.Text = analizer.ContentBaseUrl;
                this.txbContentRegex.Text = analizer.ContentRegex;
                #region 书名
                if (string.IsNullOrEmpty(this.mAnalizer.BookNamePrefix))
                    this.txbBookTitle.Text = this.mAnalizer.BookName;
                else
                    this.txbBookTitle.Text = this.mAnalizer.BookNamePrefix + " " + this.mAnalizer.BookName;
                #endregion
            }
        }

        #region 章节列表状态图片变化
        private void gvChapters_StatusChanged(DataGridViewCell cell, ChapterStatus status)
        {
            if (gvChapters.InvokeRequired)
            {
                MethodInvoker del = delegate { this.gvChapters_StatusChanged(cell, status); };
                gvChapters.Invoke(del);
            }
            else
            {
                switch (status)
                {
                    case ChapterStatus.Empty:
                        //((DataGridViewImageCell)gvChapters.SelectedRows[0].Cells[0]).Value = global::Rung.EBook.UI.Properties.Resources.ChapterType_Empty;
                        cell.Value = "E";
                        cell.Style.BackColor = Color.Gainsboro;
                        break;
                    case ChapterStatus.Downloading:
                        //((DataGridViewImageCell)gvChapters.SelectedRows[0].Cells[0]).Value = global::Rung.EBook.UI.Properties.Resources.ChapterType_Downloading;
                        cell.Value = "D";
                        cell.Style.BackColor = Color.Goldenrod;
                        break;
                    case ChapterStatus.Filled:
                        //((DataGridViewImageCell)gvChapters.SelectedRows[0].Cells[0]).Value = global::Rung.EBook.UI.Properties.Resources.ChapterType_Filled;
                        cell.Value = "F";
                        cell.Style.BackColor = Color.ForestGreen;
                        break;
                    case ChapterStatus.Unknow:
                        //((DataGridViewImageCell)gvChapters.SelectedRows[0].Cells[0]).Value = global::Rung.EBook.UI.Properties.Resources.ChapterType_Unknow;
                        cell.Value = "U";
                        cell.Style.BackColor = Color.Firebrick;
                        break;
                }
            }
        }

        internal enum ChapterStatus
        {
            Empty,
            Downloading,
            Filled,
            Unknow
        }
        #endregion

        // 取消采集
        private void btnCancelGet_Click(object sender, EventArgs e)
        {
            int count = 0;
            this.isCancel = true;

            if (this.mContentWorkers != null && this.mContentWorkers.Count > 0)
            {
                foreach (BackgroundWorker w in this.mContentWorkers)
                {
                    if (w != null && w.IsBusy)
                    {
                        //w.CancelAsync();
                        w.Dispose();
                        count++;
                    }
                }
            }
            mWriteInfo(string.Format("共注销{0}个采集线程。", count), InfoType.Warning); 

            if (this.mWorker != null && this.mWorker.IsBusy)
            {
                //w.CancelAsync();
                this.mWorker.Dispose();
                mWriteInfo("采集主线程注销成功。", InfoType.Warning); 
            }
        }

        // 注销指定的 BackgroundWorker
        private void WorkerDispose(object sender)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            if (worker != null)
            {
                if (this.mContentWorkers.Contains(worker))
                {
                    lock (OBJ_mContentWorkers)
                    {
                        if (this.mContentWorkers.Contains(worker))
                        {
                            this.mContentWorkers.Remove(worker);
                        }
                    }
                }

                worker.Dispose();
            }
        }

        private void txbContent_Set(string text)
        {
            txbContent.Invoke(new Action(() => { txbContent.Text = text; }));
        }
    }
}
